package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.tianji.learning.utils.TableInfoContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.learning.constans.SignRecordConstans.POINTS_BOARD_KEY_PRIFIX;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author wxh
 * @since 2024-08-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {
    private final StringRedisTemplate redisTemplate;
    private final UserClient userClient;

    /**
     * 查询当前榜单信息、我的排名和历史榜单信息、我的历史排名
     *
     * @param query
     * @return
     */
    @Override
    public PointsBoardVO queryPointBoard(PointsBoardQuery query) {
        //1.判断有没有seasonid（赛季id）
        boolean isTrue = ObjectUtil.isEmpty(query.getSeason()) || query.getSeason() == 0;
        //1.1构建zset的key
        LocalDateTime now = LocalDateTime.now();
        String key = POINTS_BOARD_KEY_PRIFIX + now.format(DateUtils.DATE_SUFFIX_FORMATTER);
        //2.season为0或null则为当前赛季查询我的排名或者我的历史排名
        PointsBoard myboard = isTrue ? myPointBoard(key) : myHisPointBoard(query.getSeason());
        //3.判断返回结果
        if (ObjectUtil.isEmpty(myboard)) {
            return null;
        }

        List<PointsBoard> myboardList =
                isTrue ? pointBoard(key, query.getPageNo(), query.getPageSize()) : hisPointBoard(query);
        //3.查询当前赛季的榜单和历史赛季的榜单

        //3.1收集榜单中用户id的集合
        Set<Long> userIds = new HashSet<>(myboardList.size());
        if (ObjectUtil.isNotEmpty(myboardList)) {
            userIds = myboardList.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        }
        //4.查询用户姓名
        List<UserDTO> userDTOS = userClient.queryUserByIds(userIds);
        //4.1转为Map
        Map<Long, String> userInfoMap = new HashMap<>();
        if (ObjectUtil.isNotEmpty(userDTOS)) {
            userInfoMap = userDTOS.stream().collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
        }

        //4.2分装我的排名，我的积分数据
        PointsBoardVO vo = new PointsBoardVO();
        if (ObjectUtil.isNotEmpty(myboard)) {
            vo.setPoints(myboard.getPoints());
            vo.setRank(myboard.getRank());
        }
        //4.3遍历榜单数据分装用户姓名
        //创建一个list集合
        List<PointsBoardItemVO> voList = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(userInfoMap)) {
            for (PointsBoard pointsBoard : myboardList) {
                voList.add(new PointsBoardItemVO()
                        .setName(userInfoMap.get(pointsBoard.getUserId()))
                        .setPoints(pointsBoard.getPoints())
                        .setRank(pointsBoard.getRank()));
            }
        }
        //返回vo
        return vo.setBoardList(voList);
    }

    /**
     * 查询历史赛季榜单数据
     *
     * @param query
     * @return
     */
    private List<PointsBoard> hisPointBoard(PointsBoardQuery query) {
        try {
            //拼接表名
            String tableName = "points_board_" + query.getSeason();
            //存入本地线程
            TableInfoContext.setTableName(tableName);
            //1.分页查询历史赛季榜单
            Page<PointsBoard> page = this.lambdaQuery().page(query.toMpPage());
            List<PointsBoard> records = page.getRecords();
            if (ObjectUtil.isEmpty(records)) {
                //1.1为空则返回一个空集合
                return CollUtils.emptyList();
            }
            //2.给排名赋值
            records.forEach(board -> board.setRank(board.getId().intValue()));
            return records;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BizIllegalException("数据库表不存在");
        }

    }

    /**
     * 查询当前赛季榜单数据
     *
     * @param key      zset的key
     * @param pageNo   页码
     * @param pageSize 每页条数
     * @return List<PointsBoardItemVO> 榜单数据
     */
    @Override
    public List<PointsBoard> pointBoard(String key, Integer pageNo, Integer pageSize) {
        //1.分页查询redis中的实时榜单
        //1.1设置分页参数
        int start = (pageNo - 1) * pageSize; // 0-9 ,10-19
        int end = start + pageSize - 1;
        //2.从redis中拿数据（元组）
        Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
        if (ObjectUtil.isEmpty(tuples)) {
            //为空返回一个空集合
            return CollUtils.emptyList();
        }
        int rank = start + 1;
        //2.0准备一个空集合
        List<PointsBoard> voList = new ArrayList<>(tuples.size());
        //2.1不为空遍历
        if (ObjectUtil.isNotEmpty(tuples)) {
            for (ZSetOperations.TypedTuple<String> tuple : tuples) {
                //userId
                String userId = tuple.getValue();
                //积分
                Double point = tuple.getScore();
                if (ObjectUtil.isEmpty(userId) || ObjectUtil.isEmpty(point)) {
                    continue;
                }
                //分装排名，积分
                voList.add(new PointsBoard()
                        .setPoints(point.intValue())
                        .setRank(rank++)
                        .setUserId(Long.valueOf(userId)));
            }
            pageNo++;
        }
        return voList;
    }

    /**
     * 查询历史赛季我的排名
     *
     * @param season 历史榜单id
     * @return
     */
    private PointsBoard myHisPointBoard(Long season) {
        //1.查询对应的赛季表
        //select * from points_board_8 where user_id = 2;
        //拼接表名
        String tableName = "points_board_" + season;
        //存入本地线程
        TableInfoContext.setTableName(tableName);
        PointsBoard myBoard = this.lambdaQuery()
                .select(PointsBoard::getId, PointsBoard::getPoints)
                .eq(PointsBoard::getUserId, UserContext.getUser()).one();
        if (ObjectUtil.isEmpty(myBoard)) {
            log.info("这个历史赛季没有我的排名信息");
            throw new BizIllegalException("这个历史赛季没有我的排名信息");
        }
        //返回我的历史赛季信息
        myBoard.setRank(myBoard.getId().intValue());
        return myBoard;
    }

    /**
     * 查询当前赛季我的排名
     *
     * @param key
     * @return
     */
    private PointsBoard myPointBoard(String key) {
        //1.获取userId
        String userId = UserContext.getUser().toString();
        //2.查需redis中zset的数据
        //2.1拿到积分
        Double point = redisTemplate.opsForZSet().score(key, userId);
        //2.2拿到排名
        Long rank = redisTemplate.opsForZSet().reverseRank(key, userId);
        //3.分装数据并返回
        return new PointsBoard()
                .setPoints(point == null ? 0 : point.intValue())
                .setRank(rank == null ? 0 : rank.intValue());
    }

    /**
     * Xxljob定时任务创建历史版榜单表
     *
     * @param boardName
     */
    @Override
    public void createTbale(String boardName) {
        this.baseMapper.createTable(boardName);
    }
}
